package chen.yi.component.mysql.actuator;

import chen.yi.component.common.comparator.EntityComparator;
import chen.yi.component.mysql.check.ForeignKeyUtils;
import chen.yi.component.mysql.check.IndexCheckUtils;
import chen.yi.component.mysql.check.TableCheckUtils;
import chen.yi.component.mysql.mapper.DDLBaseUtils;
import chen.yi.component.mysql.mapper.ForeignInfoMapper;
import chen.yi.component.mysql.mapper.IndexInfoMapper;
import chen.yi.component.mysql.mapper.TableInfoMapper;
import chen.yi.component.mysql.po.FieldPO;
import chen.yi.component.mysql.po.ForeignKeyPO;
import chen.yi.component.mysql.po.IndexPO;
import chen.yi.component.mysql.po.TablePO;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static chen.yi.component.mysql.constant.TemplateName.*;

/**
 * DDL语句执行器
 *
 * @author huat
 **/
@Slf4j
@Component
public class DDLActuator extends DDLBaseUtils {

    private final TableInfoMapper tableInfoMapper;
    private final IndexInfoMapper indexInfoMapper;
    private final ForeignInfoMapper foreignInfoMapper;

    @Autowired
    public DDLActuator(TableInfoMapper tableInfoMapper, IndexInfoMapper indexInfoMapper, ForeignInfoMapper foreignInfoMapper) {
        this.tableInfoMapper = tableInfoMapper;
        this.indexInfoMapper = indexInfoMapper;
        this.foreignInfoMapper = foreignInfoMapper;
    }

    /**
     * 判断表是否存在
     * @param tableName 表名
     * @return true为存在，false为不存在
     */
    public boolean existTable(String tableName) {
        String tableByName = tableInfoMapper.existTable(tableName.trim());
        return StrUtil.isNotBlank(tableByName);
    }
    /**
     * 获取创建表sql语句
     * @param tablePO 表实体类
     * @return 生成的建表语句
     */
    public String getCreateTableSql(TablePO tablePO) {
        log.info("获取建表sql语句 tablePO:【{}】", tablePO);
        TableCheckUtils.checkCreateTableInfo(tablePO);
        return templateData(CREATE_TABLE, tablePO);
    }

    /**
     * 生成建表语句并执行到数据库
     *
     * @param tablePO 表PO
     */
    public void createTable(TablePO tablePO) {
        String createTable = getCreateTableSql(tablePO);
        log.info("执行建表语句 createTable:【{}】",createTable);
        executeOperateSql(createTable);
    }

    /**
     * 获取修改表信息sql
     * @param tablePO 表信息实体类
     * @return 生成的修改表语句
     */
    public String getModifyTableSql(TablePO tablePO) throws IllegalAccessException {
        log.info("获取修改表语句 tablePO:【{}】", tablePO);
        TableCheckUtils.checkModifyTableInfo(tablePO);
        TablePO tableInfoByName = getTableInfoByName(tablePO.getOldTableName());
        if(Objects.isNull(tableInfoByName)) {
            throw new IllegalAccessException("表不存在");
        }
        log.info("表名 tableInfoByName:【{}】",tableInfoByName);
        Map<String, Object> difference = EntityComparator.differenceEntity(tableInfoByName, tablePO,true);
        return templateData(MODIFY_TABLE, difference);
    }

    /**
     * 修改表信息
     * @param tablePO 表信息
     */
    public void modifyTable(TablePO tablePO) throws IllegalAccessException {
        String modifyTableSql = getModifyTableSql(tablePO);
        log.info("修改表sql sql:【{}】",modifyTableSql);
        executeOperateSql(modifyTableSql);
    }

    /**
     * 删除表
     * @param tableName 表名
     */
    public void dropTable(String tableName){
        tableInfoMapper.dropTable(tableName);
    }

    /**
     * 获取表信息
     * @param tableName 表名
     * @return 表信息
     */
    public TablePO getTableInfoByName(String tableName) {
        log.info("通过表名 tableName:【{}】获取表结构",tableName);
        TablePO tableInfoByName = tableInfoMapper.getTableInfoByName(tableName);
        String collation  = tableInfoByName.getCollation();
        tableInfoByName.setCharset(collation.split("_")[0]);
        return tableInfoByName;
    }

    /**
     * 通过表名获取字段
     * @param tableName 表名
     * @return 字段信息
     */
    public List<FieldPO> getFieldByTableName(String tableName) {
        return tableInfoMapper.getFieldByTableName(tableName);
    }

    /**
     * 通过表名和列名删除列
     * @param tableName 表名
     * @param fieldName 列名
     */
    public void deleteFieldByName(String tableName,String fieldName) {
        tableInfoMapper.deleteFieldByName(tableName,fieldName);
    }

    /**
     * 获取保存或新建列sql
     * @param tableName 表名
     * @param fieldPOList 列信息集合
     */
    public String getSaveOrUpdateFieldSQL(String tableName,List<FieldPO> fieldPOList) throws IllegalAccessException {
        log.info(" 获取保存或新建列sql tableName:【{}】 fieldPOList:【{}】",tableName,fieldPOList);
        //表中原字段
        List<FieldPO> tableFieldList = getFieldByTableName(tableName);
        //原表字段转为map key为列名 value为对象
        Map<String, FieldPO> tableFieldMap = tableFieldList.stream()
                .collect(Collectors.toMap(
                        FieldPO::getFieldName,
                        Function.identity(),
                        (existing, replacement) -> existing  // 保留旧值
                ));
        //循环查询和原表不一样的属性值
        for (FieldPO fieldPO : fieldPOList) {
            FieldPO tableFile = tableFieldMap.get(fieldPO.getOldFieldName());
            if(tableFile != null) {
                Map<String, Object> differenced = EntityComparator.differenceEntity(tableFile, fieldPO, true);
                if(!differenced.isEmpty()){
                    String oldFieldName = fieldPO.getOldFieldName();
                    differenced.put("oldFieldName", oldFieldName);
                    differenced.put("fieldType", fieldPO.getFieldType());
                    differenced.put("fieldLength", fieldPO.getFieldLength());
                    differenced.put("fieldDecimal", fieldPO.getFieldDecimal());
                    fieldPO= BeanUtil.toBean(differenced, FieldPO.class);
                    fieldPOList.removeIf(item ->StrUtil.isNotBlank(item.getOldFieldName())&& item.getOldFieldName().equals(oldFieldName));
                    fieldPOList.add(fieldPO);
                }
            }
        }

        if(fieldPOList.size()>0){
            Map<String,Object> data = new HashMap<>();
            data.put("fieldPOList", fieldPOList);
            data.put("tableName", tableName);
            return templateData(MODIFY_FIELD,data);
        }
       return "";
    }
    /**
     * 获取保存或新建列sql
     * @param tableName 表名
     * @param fieldPOList 列信息集合
     */
    public void saveOrUpdate(String tableName,List<FieldPO> fieldPOList) throws IllegalAccessException {
        String saveOrUpdateFieldSQL = getSaveOrUpdateFieldSQL(tableName, fieldPOList);
        if(StrUtil.isBlank(saveOrUpdateFieldSQL)){
            return;
        }
        executeOperateSql(saveOrUpdateFieldSQL);
    }

    /**
     * 查询全部索引
     * @param tableName 表名
     * @return 索引信息
     * @throws IllegalAccessException 异常
     */
    public List<IndexPO> getIndexByTableName(String tableName) throws IllegalAccessException {
        return indexInfoMapper.getIndexByTableName(tableName);
    }
    /**
     * 根据表名和索引名删除索引
     * @param tableName 表名
     * @param indexName 索引名
     */
    public void deleteIndexByTableName(String tableName,String indexName) throws IllegalAccessException {
         indexInfoMapper.deleteIndexByTableName(tableName,indexName);
    }

    /**
     * 查询新增索引sql
     * @param tableName 表名
     * @param indexPOList 索引集合
     */
    public String getSaveIndexByTableName(String tableName,List<IndexPO> indexPOList) throws IllegalAccessException {
        List<IndexPO> tableIndex = getIndexByTableName(tableName);
        Map<String, IndexPO> indexMap = tableIndex.stream()
                .collect(Collectors.toMap(
                        IndexPO::getIndexName,
                        Function.identity(),
                        (existing, replacement) -> existing // 保留已存在的值
                ));
        List<IndexPO> newIndex=new ArrayList<>();
        for (IndexPO indexPO : indexPOList) {
            if(!indexMap.containsKey(indexPO.getIndexName())){
                newIndex.add(indexPO);
            }
        }
        log.info("创建索引 newIndex size:【{}】",newIndex.size());
        if(newIndex.size()>0){
            IndexCheckUtils.checkIndex(newIndex);
            Map<String,Object> params = new HashMap<>();
            params.put("tableName",tableName);
            params.put("indexes",newIndex);
            return templateData(MODIFY_INDEX, params);
        }
        return "";
    }

    /**
     * 新增索引
     * @param tableName 表名
     * @param indexPOList 索引集合
     */
    public void saveIndexByTableName(String tableName,List<IndexPO> indexPOList) throws IllegalAccessException {
        String addIndexSQL = getSaveIndexByTableName(tableName, indexPOList);
        executeOperateSql(addIndexSQL);
    }

    /**
     * 获取全部外键
     * @param tableName 表名
     * @return 外键集合
     * @throws IllegalAccessException
     */
    public List<ForeignKeyPO> getForeignKeyByTableName(String tableName) throws IllegalAccessException {
        return foreignInfoMapper.getForeignKeyByTableName(tableName);
    }

    /**
     * 删除外键
     * @param tableName 表名
     * @param foreignKeyName 外键名
     */
    public void deleteForeignKeyByTableName(String tableName,String foreignKeyName) throws IllegalAccessException {
        foreignInfoMapper.deleteForeignKeyByTableName(tableName,foreignKeyName);
    }

    /**
     * 获取新增外键sql
     * @param tableName 表名
     * @param foreignKeyPOList 外键集合
     */
    public String getSaveForeignKeyByTableName(String tableName,List<ForeignKeyPO> foreignKeyPOList) throws IllegalAccessException {
        List<ForeignKeyPO> foreignKeyByTableName = getForeignKeyByTableName(tableName);
        Map<String, ForeignKeyPO> ForeignKeyMap = foreignKeyByTableName.stream()
                .collect(Collectors.toMap(
                        ForeignKeyPO::getForeignKeyName,
                        Function.identity(),
                        (existing, replacement) -> existing // 保留已存在的值
                ));
        List<ForeignKeyPO> newForeignList=new ArrayList<>();
        for (ForeignKeyPO foreignKeyPO : foreignKeyPOList) {
            if(!ForeignKeyMap.containsKey(foreignKeyPO.getForeignKeyName())){
                newForeignList.add(foreignKeyPO);
            }
        }
        log.info("外键集合 newForeignList size:【{}】",newForeignList.size());
        if(newForeignList.size()>0){
            ForeignKeyUtils.checkForeignKey(newForeignList);
            Map<String,Object> params = new HashMap<>();
            params.put("tableName",tableName);
            params.put("foreignList",newForeignList);
            return templateData(MODIFY_FOREIGN, params);
        }
        return "";
    }
    /**
     * 执行新增外键sql
     * @param tableName 表名
     * @param foreignKeyPOList 外键集合
     */
    public void saveForeignKeyByTableName(String tableName,List<ForeignKeyPO> foreignKeyPOList) throws IllegalAccessException {
        String addForeignKeySQL = getSaveForeignKeyByTableName(tableName, foreignKeyPOList);
        executeOperateSql(addForeignKeySQL);
    }
}
